/*
 * @features: 功能
 * @description: 说明
 * @Date: 2021-09-13 23:04:42
 * @Author: judu233(769471424@qq.com)
 * @LastEditTime: 2022-08-30 15:39:12
 * @LastEditors: judu233
 */

import Thor from "../../UIKill/Thor";
import UtilsTool from "../../UtilsTool";
import { ExtendsLoad } from "../CCExtends";



declare global {
    module cc {
        interface Node {
            /**渲染组件 */
            /**---- */
            $Sprite: cc.Sprite;
            $Label: cc.Label;
            $Graphics: cc.Graphics;
            $Mask: cc.Mask;
            $LabelOutline: cc.LabelOutline;
            $LabelShadow: cc.LabelShadow;
            $RichText: cc.RichText;
            $Light: cc.Light;
            $TiledMap: cc.TiledMap;
            $TiledTile: cc.TiledTile;
            $ParticleSystem: cc.ParticleSystem;
            // $ParticleSystem3D: cc.ParticleSystem3D;

            /**ui组件 */
            /**---- */
            $Widget: cc.Widget;
            $Layout: cc.Layout;
            $Canvas: cc.Canvas;
            $Button: cc.Button;
            $ProgressBar: cc.ProgressBar;
            $Scrollbar: cc.Scrollbar;
            $ScrollView: cc.ScrollView;
            $PageViewIndicator: cc.PageViewIndicator;
            $PageView: cc.PageView;
            $Slider: cc.Slider;
            $EditBox: cc.EditBox;
            $ToggleContainer: cc.ToggleContainer;
            $ToggleGroup: cc.ToggleGroup;
            $Toggle: cc.Toggle;

            /**碰撞组件 */
            /**---- */
            $BoxCollider: cc.BoxCollider;
            $CircleCollider: cc.CircleCollider;
            $PolygonCollider: cc.PolygonCollider;

            /**物理组件 */
            /**---- */
            $RigidBody: cc.RigidBody;
            $Collider: cc.Collider;
            $Joint: cc.Joint;

            /**其他组件 */
            /**---- */
            $Camera: cc.Camera;
            $AudioSource: cc.AudioSource;
            $Animation: cc.Animation;
            $MotionStreak: cc.MotionStreak;

            /**自定义的组件 */
            $VmBase: IVmBase;
            $VmLabel: IVmLabel;
            $VmEditBox: IVmEditBox;
            $VmPage: IVmPage;
            $VmProgress: IVmProgress;
            $VmSprite: IVmSprite;
            $VmState: IVmState;
            $VmToggle: IVmToggle;

            $AnimValue: AnimValueCom;
            $AnimValueLabel: AnimValueLabelCom;
            $AnimValueProgress: AnimValueProgressCom;
            $AnimValueProgressHP: AnimValueProgressHPCom;


            /**节点里所有的组件 */
            _components: IComponentType[];
            /**自定义点击事件时可标志的变量，列如 index ，默认为空*/
            clickTag: string;
            /**自定义点击事件的index ，默认为空*/
            clickIndex: number;
            /**节点名字分解后的前缀类型 */
            $eventName: string;
            /**节点名字分解后的后缀顺序类型 */
            $indexName: string;
            /**长按触摸监听时间 */
            _touchLongTimer: number;
            /**节点上监听触摸事件*/
            readonly _touchListener: ITouchListener;
            /**_objFlags */
            readonly _objFlags;
        }
    }
}
/**扩展原型组件 */
@ExtendsLoad(cc.Node)
export class NodeBaseExtends {
    static createButton(handlers: cc.Component.EventHandler[], name?: string) {
        let node = new cc.Node(name);
        let btn = node.addComponent(cc.Button);
        btn.clickEvents.push(...handlers);
        return btn;
    }

    static createSprite(sf: cc.SpriteFrame, name?: string) {
        let node = new cc.Node(name);
        let sp = node.addComponent(cc.Sprite);
        sp.spriteFrame = sf;
        return sp;
    }

    /**
     * destroy并立即remove传入节点的所有子节点
     * @param nodes 要清楚的节点
     */
    static clearChildren(...nodes: cc.Node[]) {
        nodes.forEach((e) => {
            e.destroyAllChildren();
            e.removeAllChildren();
        });
    }

    /**
     * 快速创建一个节点
     * @param info 节点信息
     * @returns 节点
     */
    static createNode(info: { name?: string, position?: cc.Vec3, size?: cc.Size, angle?: number, anchor?: cc.Vec2, parent?: cc.Node, opacity?: number, zIndex?: number }) {
        let node = new cc.Node().setInfo(info);
        return node;
    }

}
/**扩展组件实例方法 */
@ExtendsLoad(cc.Node.prototype)
export class NodeExtends {

    static addChildBind(node: cc.Node, index: number, thorCom: IThor) {
        let targetNode = (this as any as cc.Node);
        targetNode.addChild(node, index);
        //判断addchild是否含有thor脚本
        for (let com of node._components) {
            if (com instanceof Thor && com.isBindHammer) {
                targetNode[node.name] = node;
                return;
            }
        }
        targetNode[node.name] = node;
        UIKILL_INSTENCE.bindRootNode(node, thorCom);
        return node;
    }
    static addComponentBind<T>(type: { new(): any } | string, thorCom?: IThor): T {
        let targetNode = (this as any as cc.Node);
        let com = targetNode.addComponent(type as any) as any;
        UIKILL_INSTENCE.bindComToNode(com, thorCom ?? com);
        return com;
    }

    /**
     * 设置节点信息
     * @param info 节点信息
     * @returns 
     */
    static setInfo(info: { name?: string, position?: cc.Vec3, size?: cc.Size, angle?: number, anchor?: cc.Vec2, parent?: cc.Node, opacity?: number, zIndex?: number }) {
        let { name, position, size, angle, anchor, parent, opacity, zIndex } = info;
        let node = (this as any as cc.Node);
        if (node.name == 'New Node')
            node.name = name;
        if (position)
            node.position = position;
        if (parent)
            node.parent = parent;
        if (size)
            node.setContentSize(size);
        if (anchor)
            node.setAnchorPoint(anchor);
        if (angle != null)
            node.angle = angle;
        if (opacity != null)
            node.opacity = opacity;
        if (zIndex != null)
            node.zIndex = zIndex;
        return node;
    }

    /**
     * 转换一个节点坐标系到另一个节点下
     * @param targetNode 目标节点
     * @param camera 摄像机组件 （一般不需要传入该参数，如果摄像机有位移等变化才需要传入）
     */
    static converNodeToOtherNodeSpaceAR(targetNode: cc.Node, camera?: cc.Camera) {
        let self = (this as any as cc.Node);
        let pos = self.convertToWorldSpaceAR(cc.Vec3.ZERO);
        if (camera)
            camera.getScreenToWorldPoint(pos, pos) as any;
        pos = targetNode.parent.convertToNodeSpaceAR(pos);
        return pos;
    }

    /**
     * 将节点坐标转换到屏幕
     * @param camera 摄像机
     * @returns 
     */
    static converNodeToWorldSpaceAR(camera = cc.Camera.main) {
        let self = (this as any as cc.Node);
        let world = self.parent.convertToWorldSpaceAR(self.position);
        let p = camera.getWorldToScreenPoint(world);
        return p;
    }

    /**
     * 将节点坐标系转换为世界坐标系
     * @param camera 摄像机，一般不用传，除非有偏移或者移动相机
     */
    static convertNodeToWorldSpaceAR(camera?: cc.Camera) {
        let self = (this as any as cc.Node);
        let world = self.parent.convertToWorldSpaceAR(self.position);
        if (camera)
            world = camera.getScreenToWorldPoint(world) as any;
        return world;
    };

    /**
     * 将触摸点转换成节点坐标
     * @param e 触摸点
     * @param camera 相机
     */
    static converTouchPointToNode(e: cc.Touch, camera?: cc.Camera) {
        let self = (this as any as cc.Node);
        let point = e.getLocation();
        if (camera)
            point = camera.getScreenToWorldPoint(point) as any;
        point = self.parent.convertToNodeSpaceAR(point)
        return point;
    };


    /**
     * 注册指定节点，不指定回调的话，回调记得bind一下
     * @param callBack 注册回调
     * @param caller 注册者
     */
    static regTouchStartClick(callBack: (e: cc.Event.EventTouch) => void, caller?: object, time = 0.2) {
        let node = (this as any as cc.Node);
        console.log(`注册点击节点：${node.name}`);
        let throttle = UtilsTool.throttle(callBack, time);
        node.on(cc.Node.EventType.TOUCH_START, throttle, caller);
        return () => node.unRegTouchStartClick(throttle, caller);
    }

    /**
     * 注册指定节点，不指定回调的话，回调记得bind一下
     * @param callBack 注册回调
     * @param caller 注册者
     */
    static regTouchEndClick(callBack: (e: cc.Event.EventTouch) => void, caller?: object, time = 0.2) {
        let node = (this as any as cc.Node);
        console.log(`注册点击节点：${node.name}`);
        let throttle = UtilsTool.throttle(callBack, time);
        node.on(cc.Node.EventType.TOUCH_END, throttle, caller);
        return () => node.unRegTouchEndClick(throttle, caller);
    }

    /**
     * 给该节点的所有子节点注册指定节点，不指定回调的话，回调记得bind一下
     * @param callBack 注册回调
     * @param caller 注册者
     * @param isRegSelf 是否该节点也注册，默认不注册
     */
    static regTouchEndClickChild(callBack: (e: cc.Event.EventTouch) => void, caller?: object, isRegSelf = false) {
        let node = (this as any as cc.Node);
        if (isRegSelf) {
            console.log(`注册点击节点：${node.name}`);
            node.regTouchEndClick(callBack, caller);
        }
        node.children.forEach(child => child.regTouchEndClick(callBack, caller));
        return () => {
            node.children.forEach(child => child.unRegTouchEndClick(callBack, caller));
        }
    }
    /**
     * 取消注册节点，不指定回调的话，回调记得bind一下
     * @param caller 注册者
     * @param callBack 注册回调
     */
    static unRegTouchStartClick(callBack: (e: cc.Event.EventTouch) => void, caller?: object) {
        let node = (this as any as cc.Node);
        console.log(`取消注册点击节点：${node.name}`);
        node.off(cc.Node.EventType.TOUCH_START, callBack, caller);
    }
    /**
    * 取消注册节点，不指定回调的话，回调记得bind一下
    * @param caller 注册者
    * @param callBack 注册回调
    */
    static unRegTouchEndClick(callBack: (e: cc.Event.EventTouch) => void, caller?: object) {
        let node = (this as any as cc.Node);
        console.log(`取消注册点击节点：${node.name}`);
        node.off(cc.Node.EventType.TOUCH_END, callBack, caller);
    }

    /**
     * 注册节点跟随鼠标
     * @param isAnim 是否是获取增量移动，默认直接设置位置，跟随鼠标
     * @returns 
     */
    static regMoveFollow(isAnim = false) {
        let node = (this as any as cc.Node);
        let moveCall = (e) => {
            if (isAnim) {
                e.target.x += e.getDeltaX();
                e.target.y += e.getDeltaY();
            } else
                node.setPosition(e.target.parent.convertToNodeSpaceAR(e.getLocation()))
        }
        node.on(cc.Node.EventType.TOUCH_MOVE, moveCall);
        return () => {
            if (cc.isValid(node)) {
                node.off(cc.Node.EventType.TOUCH_MOVE, moveCall);
            }
        }
    }

    /**
    * 注册节点移动
    * @param caller 对象
    * @param touchCall 触摸事件
    * @param moveCall 触摸事件
    * @param endCall 触摸事件
    * @param canselCall 触摸事件
    */
    static regMove(caller: object, touchCall: (e: cc.Event.EventTouch) => void, moveCall: (e: cc.Event.EventTouch) => void, endCall: (e: cc.Event.EventTouch) => void, canselCall?: (e: cc.Event.EventTouch) => void) {
        let node = (this as any as cc.Node);
        console.log(`节点:${node.name}注册【TOUCH_START】【TOUCH_MOVE】【TOUCH_END】事件`);
        node.on(cc.Node.EventType.TOUCH_START, touchCall, caller);
        node.on(cc.Node.EventType.TOUCH_MOVE, moveCall, caller);
        node.on(cc.Node.EventType.TOUCH_END, endCall, caller);
        if (canselCall)
            node.on(cc.Node.EventType.TOUCH_CANCEL, canselCall, caller);
        return () => {
            if (cc.isValid(node)) {
                console.log(`节点:${node.name}注销【TOUCH_START】【TOUCH_MOVE】【TOUCH_END】事件`);
                node.off(cc.Node.EventType.TOUCH_START, touchCall, caller);
                node.off(cc.Node.EventType.TOUCH_MOVE, moveCall, caller);
                node.off(cc.Node.EventType.TOUCH_END, endCall, caller);
                if (canselCall)
                    node.off(cc.Node.EventType.TOUCH_CANCEL, canselCall, caller);
            }
        }
    }

    /**
    * 设置节点是否吞噬事件
    * @param isSwallow 是否吞噬
    */
    static setSwallow(isSwallow = false) {
        let node = this as any as cc.Node & { _touchListener: any }
        if (node._touchListener) {
            node._touchListener.setSwallowTouches(isSwallow);
        } else
            console.warn(`节点：${node.name}没有注册事件，无法设置穿透`);
        // log(`设置节点:${n.name}${isSwallow ? '吞噬' : '不吞噬'}事件`);
    }

    /**
    * 深度遍历子孙节点
    * @param  cb  返回true结束遍历
    * @param  isDeepWalk 是否深度优先（默认广度优先）
    */
    static forEachChildren(_forEachCb: (child: cc.Node) => boolean | void, isDeepWalk?: boolean) {
        let _forEach = (node: cc.Node) => {
            if (!node || !_forEachCb)
                return true;
            const children = node.children;
            if (children.length) {
                if (isDeepWalk) {
                    // 深度优先,广度其次
                    for (let child of children)
                        if (_forEach(child))
                            return true;
                    for (let child of children)
                        if (_forEachCb(child))
                            return true;
                } else {
                    // 广度优先 ,深度其次
                    for (let child of children)
                        if (_forEachCb(child))
                            return true;
                    for (let child of children)
                        if (_forEach(child))
                            return true;
                }
            }
        }
        return _forEach(this as any as cc.Node);
    }

    /**
    * 查找所在节点的所有子孙节点指定的某个组件
    * @param {String} name             节点名字
    * @param {{prototype: T}} type     指定节点上的某个组件
    * @param {Boolean} multiple        是否查询多个
    */
    static findN(name: string, type: { prototype: any; }, multiple: boolean) {
        let node = (this as any as cc.Node);
        if (!name || !node.children.length) {
            return [];
        }
        let res = null;
        let result = [];
        node.forEachChildren((child) => {
            let com;
            if (!type || type === cc.Node) {
                com = child;
            } else {
                com = child.getComponent(type);
            }
            res = (child.name === name) && com;
            if (res) {
                result.push(res);
                if (!multiple) return false;
            }
        })
        return result;
    }

    /**
    * 创建一个事件句柄
    * @param comName 组件名字
    * @param callFunName 组件函数string
    * @param customEventData 自定义参数
    * @returns eventHandle
    */
    static createHandle<T extends cc.Component, K extends string>(comName: string, callFunName: keyof T, customEventData?: K) {
        let node = this as any as cc.Node;
        let com = node.getComponent(comName) ?? node.getComponent(cc.js.getClassByName(comName) as any);
        let clickHandle = new cc.Component[`EventHandler`]();
        clickHandle.target = node;
        clickHandle.component = comName; //这个脚本的类名
        clickHandle.handler = callFunName as string;
        clickHandle.customEventData = customEventData ?? '';
        clickHandle['_componentId'] = com[`__cid__`];
        return clickHandle;
    }

    /**重置节点位置 */
    static restPos() {
        let node = this as any as cc.Node;
        node.x = 0;
        node.y = 0;
    }

    /**
     * 设置节点上的图片（如果有的话）
     * @param url 要替换的图片url
     * @param size 强制设置节点尺寸，传number 表示等比设置，否则按尺寸设置
     */
    static setSf(url: string, size?: number | cc.Size) {
        let node = this as any as cc.Node;
        let sp = node.getComponent(cc.Sprite);
        sp?.setSf(url);
        if (!sp)
            console.warn(`设置节点:${node.name}图片:${url}失败`);
        else if (size != null) {
            size = size instanceof cc.Size ? size : cc.size(size, size);
            node.setContentSize(size);
        }
    }

    /**
     * 设置文本
     * @param str 要设置的文本标签
     */
    static setLb(str: string) {
        let node = this as any as cc.Node;
        let lb: cc.Label | cc.RichText = node.getComponent(cc.Label);
        if (!lb)
            lb = node.getComponent(cc.RichText);
        if (lb)
            lb.string = str;
        else
            console.warn(`设置文本失败，节点;${node.name}上没有文本组件`)
    }

    /**重置基本属性 */
    static restAll() {
        let node = this as any as cc.Node;
        node.angle = 0;
        node.opacity = 255;
        node.setPosition(cc.v2(0, 0));
        node.setScale(cc.v2(1, 1));
        node.setAnchorPoint(cc.v2(0.5, 0.5));
    }

    /**翻转x */
    static flipX() {
        let node = this as any as cc.Node;
        node.scaleX = -node.scaleX;
    }

    /**翻转y */
    static flipY() {
        let node = this as any as cc.Node;
        node.scaleY = -node.scaleY;
    }

    /**节点范围 加上锚点 */
    static set_node_world(w?: number, h?: number) {
        let node = this as any as cc.Node;
        w = w ? w : node.width * node.scaleX;
        h = h ? h : node.height * node.scaleY;
        let world = node.convertNodeToWorldSpaceAR();
        let x = world.x - w * node.anchorX;
        let y = world.y - h * node.anchorY;
        return new cc.Rect(x, y, w, h);
    };

    /**
     * 检查节点在目标节点的重合度是否符合指定值（在摄像机里判断位置）
     * @param targetNode 目标节点/坐标
     * @param targetPercent 目标重合度 0-1
     * @returns 
     */
    static checkPercent(targetNode: cc.Node | cc.Rect, targetPercent: number) {
        //调试
        // var manager = cc.director.getCollisionManager();
        // manager.enabled = true;
        // manager.enabledDebugDraw = true;
        // manager.enabledDrawBoundingBox = true;
        // if (!dragNode.getComponent(cc.BoxCollider))
        //     dragNode.addComponent(cc.BoxCollider);
        // if (!targetNode.getComponent(cc.BoxCollider))
        //     targetNode.addComponent(cc.BoxCollider);

        //检查重合度
        let dragNode = this as any as cc.Node;
        let dragRect = dragNode.set_node_world();
        let targetRect = cc.rect(0, 0, 0, 0);
        if (targetNode instanceof cc.Node)
            targetRect = targetNode.set_node_world();
        else
            targetRect = targetNode;
        // if (dragRect.x > targetRect.x + targetRect.width) { return 0.0; }
        // if (dragRect.y > targetRect.y + targetRect.height) { return 0.0; }
        // if (dragRect.x + dragRect.width < targetRect.x) { return 0.0; }
        // if (dragRect.y + dragRect.height < targetRect.y) { return 0.0; }
        let colInt = Math.min(dragRect.x + dragRect.width, targetRect.x + targetRect.width) - Math.max(dragRect.x, targetRect.x);
        let rowInt = Math.min(dragRect.y + dragRect.height, targetRect.y + targetRect.height) - Math.max(dragRect.y, targetRect.y);
        let intersection = colInt * rowInt;
        let area1 = dragRect.width * dragRect.height;
        let area2 = targetRect.width * targetRect.height;
        let curPercent = intersection / (area1 + area2 - intersection);
        cc.log(`重合度：${curPercent}`);

        let ds = this.computRectJoinUnion(dragRect, targetRect)

        return curPercent > targetPercent;
    }

    /**
     * @brief 计算两个矩形的相交面积及组合面积，同时计算相交面积占组合面积的比例
     * @param 第一个矩形的位置
     * @param 第二个矩形的位置
     * @param 两个矩阵相交的面积大小
     * @param 两个矩阵组合的面积大小
     * @return 两个矩阵相交面积占组合面积的比例,即重合比例。如果组合面积为0，则返回0
     */
    static computRectJoinUnion(rc1, rc2) {
        //p1为相交位置的左上角坐标，p2为相交位置的右下角坐标
        let p1 = cc.v3(Math.max(rc1.x, rc2.x), Math.max(rc1.y, rc2.y));
        let p2 = cc.v3(Math.min(rc1.x + rc1.width, rc2.x + rc2.width), Math.min(rc1.y + rc1.height, rc2.y + rc2.height));
        let AJoin = 0;
        //判断是否相交,如果先交，求出相交面积
        if (p2.x > p1.x && p2.y > p1.y) {
            AJoin = (p2.x - p1.x) * (p2.y - p1.y);
        }
        let A1 = rc1.width * rc1.height;
        let A2 = rc2.width * rc2.height;
        //两者组合的面积
        let AUnion = (A1 + A2 - AJoin);
        //相交面积与组合面积的比例
        if (AUnion > 0)
            return (AJoin / AUnion);
        else
            return 0;
    }

    /**
     * 获取指定节点的所有子节点（）
     * @param node 节点
     * @param isIncludeCom 节点
     * @returns 
     */
    static getAllChildNodeAndCom(node: cc.Node, isIncludeCom = false) {
        // let nodeList: cc.Node[] = [];
        // let comList: cc.Component[] = [];
        // let getChildNode = (targetNode: cc.Node) => {
        //     nodeList.push(targetNode);
        //     if (isIncludeCom)
        //         comList.push(...targetNode[`_components`]);
        //     for (let childNode of targetNode.children)
        //         getChildNode(childNode);
        // }
        // getChildNode(node);
        // return { nodeList, comList };
    }

    /**获取该节点在父节点上的index */
    static getIndexByParent() {
        let node = this as any as cc.Node;
        return node.parent.children.findIndex(n => n == node);
    }

    /*** 获取节点的全路径*/
    static getFullPath() {
        let array = [];
        let node = this as any as cc.Node;
        do {
            array.unshift(node.name);
            node = node.parent;
        } while (node && node.name !== 'Canvas')
        return array.join('/');
    }
}
